#include <iostream>
#include <string>
#include <pthread.h>
#include <vector>
#include <unistd.h>
//等待本身也是在访问临界资源，所以要在获取锁释放锁
//之间等待。等待的线程会成为一个队列，当有锁的资源可供获得时，
//在这个等待队列头上的线程就可以获得这把锁，去访问临界资源
//生产者、消费者模型
//基本理解：生产者消费者交易场所
//321原则：
//3个角色之间的关系：
//生产者和生产者之间是互斥关系和竞争关系：一方把交易场所占满了，其他生产者就不能
//在使用这块交易场所了，而竞争就是争得这块交易场所
//生产者和消费者之间的关系：互斥关系和同步关系。生产者在往交易场所放数据的
// 时候，如果数据还没放完消费者就来获取数据，那么就很容易造成数据不一致问题
//消费者和消费者之间：互斥关系。虽然都在去数据，但是数据是有限的，你取完
//以后我就不一定去的到了
//生产者和消费者两个对象之间的：
//两者都是线程，一个往交易场所里写数据，另一个从交易场所中读取数据
//一个消费场所：
//具有某种数据格式的内存空间
//优点：允许忙闲不均，生产者消费者解耦合，效率高

//通过条件变量访问的队列也是临界资源，所以访问时也要加锁。
//在交易场所时，如果交易场所满了，那就会在条件变量处等待；等到线程
//被唤醒的时候，默认就在临界区被唤醒，需要重新申请锁，如果申请锁
//失败，那么就在锁处等待。
//但是pthread_cond_wait也是函数调用，是函数调用就有可能调用失败；
//而且在多线程情况下，有可能会发生误触发pthread_cond_signal导致
//阻塞队列中不满足唤醒条件的线程被伪唤醒；解决条件就是进行二次判断，
//可以利用while循环的二次判断属性来保证即使是伪唤醒也会再次进入等待
//我们通过这种方式实现的

//多线程使用资源有两种场景：
//将目标资源整体使用（所以需要加锁，或者使用锁+二元信号量）
//将目标资源按照不同的块，分批使用（信号量）

//信号量的p（--）操作和v（++）操作都是原子的
//因为信号量本质上是一个计数器，所以他也是临界资源

//CP&&基于环形队列的生产者消费者模型
//用模运算+数组实现模拟环形队列
//四个约定：
//空时，生产者先行；满时，消费者先行；生产者不能套消费者一圈以上，否则会导致数据丢失
//消费者不能超过生产者
//只要我们不访问同一个位置，我们就可以同时进行
//什么时候我们会在同一个位置呢？为空或者为满的时候，这时生产消费就可以同时运行
//为空：只能互斥，让生产者先运行
//为满：只能互斥，让消费者先运行
//结论一：环形队列不为空不为满是，消费生产可同时运行
//结论二：环形队列为空或者为满的时候，生产消费需要同步、互斥

//信号量的接口：
//


int count = 1000;
const int NUM = 10;
pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;

void* pthread_run(void* arg)
{
    std::string name = (char*)arg;
    while(1)
    {
        pthread_mutex_lock(&glock);
        pthread_cond_wait(&gcond, &glock);
        if(count > 0)
        {
            std::cout << "进程:" << name << "计算:" << count << std::endl;
            count--;
            sleep(10);
            pthread_mutex_unlock(&glock);
        }
        else
        {
            break;
        }
    }
    return nullptr;
}

int main()
{
    std::vector<pthread_t> thread_pool;
    for(int i = 0; i < NUM; i++)
    {
        pthread_t tid;
        char* name = new char[64];
        snprintf(name, 64, "thread_%d", i);
        pthread_create(&tid, nullptr, pthread_run, (void*)name);
        thread_pool.push_back(tid);
    }

    while(1)
    {
        int n = pthread_cond_signal(&gcond);
        if(n == 0)
        {
            std::cout << "唤醒了一个线程" << std::endl;
        }
        sleep(1);
    }

    for(auto& e : thread_pool)
    {
        pthread_join(e, nullptr);
    }
    return 0;
}
